package com.mapforge.selection;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Iterator;

public class SinglePointSelection implements Selection {

	boolean isEmpty;
	private ComparablePoint point;

	public SinglePointSelection() {
		this.point = new ComparablePoint();
		this.isEmpty = true;
	}

	/**
	 * Constructs a new {@link SinglePointSelect} with the specified point, and
	 * a value indicating whether the selection is empty or not.
	 * 
	 * @param point
	 *            specified point this selection represents
	 * @param empty
	 *            whether selection is valid
	 */
	public SinglePointSelection(Point point) {
		this(point.x, point.y);
	}

	public SinglePointSelection(int x, int y) {
		this.point = new ComparablePoint(x, y);
		this.isEmpty = false;
	}

	public Rectangle getBoundingRectangle() {
		if (!isEmpty) {
			return new Rectangle(point.x, point.y, 1, 1);
		} else {
			return new Rectangle();
		}
	}

	@Override
	public void setOrigin(int x, int y) {
		this.point.setLocation(x, y);
	}

	@Override
	public Iterator<ComparablePoint> iterator() {
		return new SinglePointIterator();
	}

	@Override
	public Iterator<ComparablePoint> iterator(Rectangle rectangle) {
		if (rectangle.contains(point)) {
			return iterator();
		} else {
			return new NullIterator<ComparablePoint>();
		}
	}

	@Override
	public void clearSelection() {
		this.point.setLocation(0, 0);
		isEmpty = true;
	}

	@Override
	public boolean add(Selection other) {
		// selection is empty
		if (isEmpty) {
			Iterator<ComparablePoint> otherIT = other.iterator();
			// check if passed selection has exactly one point
			if (otherIT.hasNext()) {
				ComparablePoint p = otherIT.next();
				if (!otherIT.hasNext()) {
					this.point = p;
					this.isEmpty = false;
					return true;
				}
				// passed selection has more than one point.
				else {
					throw new IllegalArgumentException(
							"Single point selection cannot contain more than one point");
				}
			}
			// other selection is empty
			else {
				return false;
			}
		}
		// this selection isn't empty
		else {
			throw new IllegalArgumentException(
					"Single point selection cannot contain more than one point");
		}
	}

	@Override
	public boolean subtract(Selection other) {
		if (other.contains(point.x, point.y)) {
			clearSelection();
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean intersect(Selection other) {
		// we can't add any points to this selection.
		return false;
	}

	public boolean intersects(Selection other) {
		return other.contains(point.x, point.y);
	}

	@Override
	public boolean contains(int x, int y) {
		return point.x == x && point.y == y;
	}

	private class SinglePointIterator implements Iterator<ComparablePoint> {
		boolean hasNext;

		public SinglePointIterator() {
			hasNext = true;
		}

		@Override
		public boolean hasNext() {
			return hasNext;
		}

		@Override
		public ComparablePoint next() {
			hasNext = false;
			return new ComparablePoint(point);
		}

		@Override
		public void remove() {
			clearSelection();
		}

	}

	@Override
	public boolean isEmpty() {
		return isEmpty;
	}

	@Override
	public Selection addition(Selection other) {
		DiscontinuousSelectionCollection selection = new DiscontinuousSelectionSet();
		selection.addPoint(new ComparablePoint(point));
		Iterator<ComparablePoint> it = other.iterator();
		while (it.hasNext()) {
			selection.addPoint(it.next());
		}
		return selection;
	}

	@Override
	public Selection subtraction(Selection other) {
		DiscontinuousSelectionCollection selection = new DiscontinuousSelectionSet();
		Iterator<ComparablePoint> it = other.iterator();
		ComparablePoint p;
		while (it.hasNext()) {
			p = it.next();
			// add everything but the point we know about
			if (!point.equals(p)) {
				selection.addPoint(p);
			}
		}
		return selection;
	}

	@Override
	public Selection intersection(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	public String toString() {
		return super.toString() + "[" + point.x + ", " + point.y + "]";
	}
}
